package com.mathdroid.modules.sniper;

import java.util.LinkedList;

import com.mathdroid.model.Model;
import com.mathdroid.model.abilities.Ability;
import com.mathdroid.modules.EExecutionResult;
import com.mathdroid.modules.Module;
import com.mathdroid.modules.SimulationEvent;

/**
 * The module for the Imperial Agent Sniper.
 */
public final class ModuleSniper extends Module {
	
	/**
	 * Generated UID.
	 */
	private static final long serialVersionUID = -3774252957734571577L;
	
	/**
	 * One second in milliseconds.
	 */
	private static final double SECOND = 1000d;
	
	/**
	 * The starting energy should be 100 energy. This is a full energy bar.
	 */
	private static final double MAX_ENERGY = 100d;
	
	/**
	 * The energy value for high ticks.
	 */
	private static final double HIGH_ENERGY_TICK = 5d;
	
	/**
	 * The threshold for high energy ticks. Ticks above this threshold are 
	 * better.
	 */
	//TODO double check thresholds, those should be percentual
	private static final double HIGH_ENERGY_THRESHOLD = 60d; 
	
	/**
	 * The threshold for low energy ticks. Ticks below this threshold are 
	 * worse.
	 */
	private static final double LOW_ENERGY_THRESHOLD = 20d;
	
	/**
	 * The energy value for low ticks.
	 */
	private static final double LOW_ENERGY_TICK = 2d;
	
	/**
	 * The energy value for medium ticks.
	 */
	private static final double MEDIUM_ENERGY_TICK = 3d;
	
	/**
	 * The energy bar. It starts at full capacity.
	 */
	private double energy = MAX_ENERGY;
	
	@Override
	public void before() {
		
		/* Register the abilities to the manager. */
		final LinkedList<Ability> priorityList = 
			Model.getProfile().getAbilities();
		
		for (Ability a : priorityList) {
			this.registerAbility(a.clone());
		}
	}
	
	@Override
	public void cleanUp() {
		this.energy = MAX_ENERGY;
	}

	@Override
	public void simulate() {
		SimulationEvent e = null;
		
		while (this.iterateTime()) {
			this.manageEnergyTicks();
			
			for (Ability a : this.getManagedAbilities()) {
				e = this.tryToExecute(a);
				
				if (e.getResult() != EExecutionResult.FAIL) {
					break;
				}
			}
		}
	}

	/**
	 * Every second this method adds energy to the energy bar due to energy 
	 * ticks.
	 */
	private void manageEnergyTicks() {
		if (this.whatTimeIsIt() % SECOND == 0) {
			this.energy += this.getEnergyTickIncrease();
			if (this.energy > MAX_ENERGY) {
				this.energy = MAX_ENERGY;
			}
		}
	}

	/**
	 * This method calculates the increase in energy from the energy tick. 
	 * This depends on the state of the energy bar.
	 * Filled energy bar: Higher energy ticks.
	 * Less filled energy bar: Lower energy ticks.
	 * @return Energy tick size.
	 */
	private double getEnergyTickIncrease() {
		double val = 0;
		
		//TODO NYI values:
		if (this.energy >= HIGH_ENERGY_THRESHOLD) {
			val = HIGH_ENERGY_TICK;
		} else if (this.energy < LOW_ENERGY_THRESHOLD) {
			val = LOW_ENERGY_TICK;
		} else {
			val = MEDIUM_ENERGY_TICK;
		}
		
		return val;
	}
	
	@Override
	public Module getInstance() {	
		return new ModuleSniper();
	}

	@Override
	public boolean isExecutable(final Ability a) {
		boolean executable = true;
		
		if (this.energy - a.getCost() < 0.0) {
			executable = false;
		}
		
		return executable;
	}

	@Override
	public SimulationEvent execute(final Ability a) {
		final SimulationEvent e = new SimulationEvent();
		
		this.energy -= a.getCost();
		//TODO ----
		this.log("Executed " + a.getName());
		this.getStatistics().get(a.getName()).addCosts(a.getCost());
		
		e.setIdleTimeMs(a.getCastTimeMs());
		
		if (a.affectsGCD() && e.getIdleTimeMs() < GCD_DURATION_MS) {
			e.setIdleTimeMs(GCD_DURATION_MS);
		}
		
		return e;
	}
}
